package com.logansoft.zhxypkoa.components;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.SecretKey;

import org.springframework.stereotype.Component;

/**
 * JWT Token 工具
 * 
 * @author zhuyj
 *
 */
@Slf4j
@Component
public class JwtTool implements Serializable {

  private static final long serialVersionUID = -3301605591108950415L;

  private static final String CLAIM_KEY_USERNAME = "sub";
  private static final String CLAIM_KEY_CREATED = "created";

  private String secret = "Secret__345keySecretSecret__345keySecret";

  private Long expiration = (long) 6000;

  public String getUsernameFromToken(String token) {
    String username;
    try {
      final Claims claims = getClaimsFromToken(token);
      username = claims.getSubject();
    } catch (Exception e) {
      username = null;
    }
    log.debug("getUsernameFromToken: " + username);
    return username;
  }

  public Date getCreatedDateFromToken(String token) {
    Date created;
    try {
      final Claims claims = getClaimsFromToken(token);
      created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
    } catch (Exception e) {
      created = null;
    }
    log.debug("getCreatedDateFromToken: " + created);
    return created;
  }

  public Date getExpirationDateFromToken(String token) {
    Date expiration;
    try {
      final Claims claims = getClaimsFromToken(token);
      expiration = claims.getExpiration();
    } catch (Exception e) {
      expiration = null;
    }
    log.debug("getExpirationDateFromToken: " + expiration);
    return expiration;
  }

  private Claims getClaimsFromToken(String token) {
    Claims claims;
    try {
      SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
      claims = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
    } catch (Exception e) {
      claims = null;
    }
    return claims;
  }

  private Date generateExpirationDate() {
    return new Date(System.currentTimeMillis() + expiration * 1000);
  }

  private Boolean isTokenExpired(String token) {
    final Date expiration = getExpirationDateFromToken(token);
    return expiration.before(new Date());
  }

  private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
    return (lastPasswordReset != null && created.before(lastPasswordReset));
  }

  public String generateToken(JwtUser jwtUser) {
    Map<String, Object> claims = new HashMap<>();
    // claims.put(CLAIM_KEY_USERNAME, jwtUser.getUsername());
    claims.put(CLAIM_KEY_USERNAME, jwtUser.getId());
    claims.put(CLAIM_KEY_CREATED, new Date());
    return generateToken(claims);
  }

  String generateToken(Map<String, Object> claims) {
    // String base64EncodedSecretKey = base64Encode(secret);
    // byte[] keyBytes = Decoders.BASE64.decode(base64EncodedSecretKey);
    SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
    log.debug("generateToken: " + key.toString());
    return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate())
        .signWith(key).compact();
  }

  public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
    final Date created = getCreatedDateFromToken(token);
    return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset) && !isTokenExpired(token);
  }

  public String refreshToken(String token) {
    String refreshedToken;
    try {
      final Claims claims = getClaimsFromToken(token);
      claims.put(CLAIM_KEY_CREATED, new Date());
      refreshedToken = generateToken(claims);
    } catch (Exception e) {
      refreshedToken = null;
    }
    return refreshedToken;
  }

  public Boolean validateToken(String token, JwtUser jwtUser) {
    final String username = getUsernameFromToken(token);
    final Date created = getCreatedDateFromToken(token);
    // final Date expiration = getExpirationDateFromToken(token);
    return (username.equals(jwtUser.getId()) && !isTokenExpired(token)
        && !isCreatedBeforeLastPasswordReset(created, jwtUser.getLastPasswordResetDate()));
  }
}